Skip to content

Design document for Objective-C interop #44934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

AaronRobinsonMSFT
Copy link
Member

See #44659

@AaronRobinsonMSFT
Copy link
Member Author

AaronRobinsonMSFT commented Nov 19, 2020

@AaronRobinsonMSFT
Copy link
Member Author

@tommcdon @noahfalk There would be some diagnostic asks for this work. Similar commands to the ComWrappers API. See bottom of the document for proposed commands.

@noahfalk
Copy link
Member

@tommcdon @noahfalk There would be some diagnostic asks for this work. Similar commands to the ComWrappers API. See bottom of the document for proposed commands.

Thanks @AaronRobinsonMSFT for the heads up : ) I am about to be on vacation for end of the year but I can take a closer look when I am back or @tommcdon might ask one of the other devs to look into it. I'm assuming this has a generic .NET 6 timeline unless you say otherwise?

@AaronRobinsonMSFT
Copy link
Member Author

AaronRobinsonMSFT commented Nov 28, 2020

I'm assuming this has a generic .NET 6 timeline unless you say otherwise?

@noahfalk Yes. This is for .NET 6. I don't think, naively, this would represent a large time investment from the Diagnostics team. I am assuming it would be similar to the ComWrappers support - #42942 #43164.

* Hardcode these special cases in the runtime with the assembly code.
* Hardcode these special cases in the runtime but permit the "overrides" to be provided through a managed API.

The last option seems to have the most flexibility and follows the "pay-for-play" principle since some users may not care about exception handling or variadic argument support.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does PINVOKE_OVERRIDE fit into this vs the approach in #47721? There is quite an overlap.

PINVOKE_OVERRIDE could easily be used by the native code to implement the same logic as mono_dllmap_insert used. It requires support from the host though.

I am not convinced that a managed interface is very helpful in this case. The objc_* methods themselves still have to be implemented in the native code due to the nature of the argument dispatch. Who would register the callbacks and at what time would that happen? If the answer is that it's done once at startup and driven by the host then I think the approach is unnecessarily complicated and duplicates the functionality that is already available through other means.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PINVOKE_OVERRIDE logic was leveraged to implement this feature. The overlap here is related to implementation detail only since the PINVOKE_OVERRIDE feature isn't publicly exposed. There is no desire to presently expose that and thus we permit a narrow override for this specific scenario. I am assuming that PINVOKE_OVERRIDE is referring to what is used to handle single file work.

I am not convinced that a managed interface is very helpful in this case.

CoreCLR is not exposing functionality through native means. Using UnmanagedCallersOnly and function pointers exposing most functionality to native can be done in a bespoke manner based on a consumer's needs.

I am not convinced that a managed interface is very helpful in this case. The objc_* methods themselves still have to be implemented in the native code due to the nature of the argument dispatch.

They don't actually need to be implemented in native at all depending on what one wants to do with them. We have various tests that implement them in managed code using UnmanagedCallersOnly. This isn't to say we can handle all functionality but most is possible at least with what we've prototyped - perhaps there is an example?

Who would register the callbacks and at what time would that happen? If the answer is that it's done once at startup and driven by the host then I think the approach is unnecessarily complicated and duplicates the functionality that is already available through other means.

Yep, this must be done relatively early.

Copy link
Member

@jkotas jkotas Feb 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PINVOKE_OVERRIDE is for unmanaged hosts. It cannot be really replaced by managed code for all cases. The host cannot always inject the managed code soon enough for it to kick in.

Our original design goal was to make Objective-C interop work without special host. For example, to allow using libraries that use nice Objective-C interop in regular macOS console applications, with regular host that is not aware that Objective-C interop exists. It is how we have the designed the Com/WinRT interop for .NET 5 and we are happy with it.

It is not clear whether we will be able to maintain this goal due to resource constrains. If we end up giving up on this goal and make the Objective-C interop work with just the Xamarin/Apple app host, I agree that this method is not really needed. We can just use PINVOKE_OVERRIDE.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't actually need to be implemented in native at all depending on what one wants to do with them.

I have seen the code in the tests of the feature/objc-interop branch but I had the feeling that it covers only narrow scenario. I was thinking specifically about the implementation in Xamarin that does translation of Objective-C exceptions. I may need to read more into it though.

Our original design goal was to make Objective-C interop work without special host.

I think this somewhat slipped off my mind and it's a valid point. On the other hand it feels like SetMessageSendCallback specifically is problematic if two different libraries tried to use it. Likely not a big deal in practice but I remember seeing the concern in some comment earlier.

Copy link
Member Author

@AaronRobinsonMSFT AaronRobinsonMSFT Feb 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking specifically about the implementation in Xamarin that does translation of Objective-C exceptions.

If one wants exceptions support, I agree that is likely not possible to implement in C#. However that should not be the reason for forcing it be a hard requirement.

On the other hand it feels like SetMessageSendCallback specifically is problematic if two different libraries tried to use it.

Yes that is true. This API is really a niche case for Xamarin at this point. The ComWrappers also has an issue with singleton usage - the GC interaction model. It was a desire to make that more narrow but given Xamarin established guidance around usage of P/Invoke with Selectors it is hard to have a model that can work with multiple consumers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess my point was that if it's primarily for the Xamarin use-case then PINVOKE_OVERRIDE would work there just as good. It has the additional benefit of being slightly more flexible and guaranteeing the registration soon enough to ensure that all P/Invokes are consistently resolved (and thus it's easier to debug and reason about). Since there can only be one host it also takes care of the issue of two consumers trying to use the interface. Lastly, it's available on Mono and CoreCLR today so the implementation for both runtimes could be easily shared on the Xamarin side.

Obviously that was all under the assumption that one would have the control of the unmanaged host. If this is supposed to cover other scenarios like running under the default CoreCLR host (corehost, corerun or other) and possibly a managed implementation of the callbacks then I see the reason for the different approach.

I am still unsure what can you actually achieve in the managed code and how would you handle all the cases of parameter passing and forwarding that to the underlying implementation. The unit test doesn't seem to cover that ground.

Base automatically changed from master to main March 1, 2021 09:07
@danmoseley
Copy link
Member

@AaronRobinsonMSFT would it make sense to merge this as-is (or possibly close) if it's not an active PR?

@AaronRobinsonMSFT
Copy link
Member Author

@danmoseley I can close it. We are close to getting to a place where the doc can be updated with the agreed upon solution but it will require another draft. I will resubmit when appropriate.

@danmoseley
Copy link
Member

One thing to consider is whether dotnet/designs is a better home. The pivot we agreed on is that the code-repo is appropriate if it's a living document that must be kept in sync with code; otherwise dotnet/designs is preferred. It doesn't need to be a cross-repo design, eg.

@AaronRobinsonMSFT
Copy link
Member Author

@danmoseley Moved to dotnet/designs#197.

@ghost ghost locked as resolved and limited conversation to collaborators May 7, 2021
@AaronRobinsonMSFT AaronRobinsonMSFT deleted the objc_interop_design branch September 2, 2021 04:30
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants